package search

import (
	b "code.google.com/p/gochess/board"
	"code.google.com/p/gochess/movement"
	pos "code.google.com/p/gochess/position"
	"fmt"
)

type position struct {
	pos.Position
	hash          zobristHash
	pawnHash      zobristHash
	material      [pos.BiggestColor + 1]int32
	positionBonus [pos.BiggestColor + 1]int32
}

// Position holds a copy of the position parameter.
// this ensures the whole data structure is in one place in memory.
func newPosition(p *pos.Position) *position {

	position := position{Position: *p, hash: computeHash(p), pawnHash: computePawnHash(p)}

	for rank := b.Rank1; rank <= b.Rank8; rank++ {
		for file := b.A; file <= b.H; file++ {
			square := b.GetSquare(file, rank)
			piece := p.Get(square)
			if piece != pos.NoPiece {
				position.material[piece.Color()] += value(piece)
				position.positionBonus[piece.Color()] +=
					movement.PositionBonus(piece, square)

			}
		}
	}
	return &position
}

func (this *position) Hash() zobristHash {
	return this.hash
}

func (this *position) PawnHash() zobristHash {
	return this.pawnHash
}

func (this *position) Material(color pos.Color) int32 {
	return this.material[color]
}

func (this *position) PositionBonus(color pos.Color) int32 {
	return this.positionBonus[color]
}

func (this *position) SetEnPassantTarget(new b.Square) {
	this.hash.updateEnPassantTarget(this.Position.EnPassantTarget(), new)
	this.Position.SetEnPassantTarget(new)
}

func (this *position) SetCastlingRights(new pos.CastlingRights) {

	if this.Position.CastlingRights() != new {
		this.hash.updateCastlingRights(this.Position.CastlingRights(), new)
		this.Position.SetCastlingRights(new)
	}
}

func (this *position) SwitchSides() {
	this.hash.switchSides()
	this.Position.SwitchSides()
}

func (this *position) Set(piece pos.Piece, location b.Square) {

	if piece.Type() == pos.Pawn {
		this.pawnHash.setPiece(piece, location)
	}
	this.hash.setPiece(piece, location)
	this.material[piece.Color()] += value(piece)
	this.positionBonus[piece.Color()] += movement.PositionBonus(piece, location)
	this.Position.Set(piece, location)
}

func (this *position) Reset(piece pos.Piece, location b.Square, index int8) {

	if piece.Type() == pos.Pawn {
		this.pawnHash.setPiece(piece, location)
	}
	this.hash.setPiece(piece, location)
	this.material[piece.Color()] += value(piece)
	this.positionBonus[piece.Color()] += movement.PositionBonus(piece, location)
	this.Position.Reset(piece, location, index)
}

func (this *position) Remove(location b.Square) int8 {

	piece := this.Position.Get(location)
	if piece.Type() == pos.Pawn {
		this.pawnHash.removePiece(piece, location)
	}
	this.hash.removePiece(piece, location)
	this.material[piece.Color()] -= value(piece)
	this.positionBonus[piece.Color()] -= movement.PositionBonus(piece, location)
	return this.Position.Remove(location)
}

func (this *position) Move(start, dst b.Square) {

	piece := this.Position.Get(start)

	if piece.Type() == pos.Pawn {
		this.pawnHash.movePiece(piece, start, dst)
	}
	this.hash.movePiece(piece, start, dst)

	this.positionBonus[piece.Color()] +=
		movement.PositionBonus(piece, dst) - movement.PositionBonus(piece, start)

	this.Position.Move(start, dst)

}

func (this *position) String() string {

	output := fmt.Sprintf("%v\nPosition hash: %v, Pawn hash: %v\n",
		&this.Position, this.hash, this.pawnHash)
	output += fmt.Sprintf("Material White: %v, Black: %v; ",
		this.material[pos.White], this.material[pos.Black])
	return output + fmt.Sprintf("Position bonus White: %v, Black: %v",
		this.positionBonus[pos.White], this.positionBonus[pos.Black])
}
